package org.example.learn.javassist.monitor;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class GenerateClassMethod extends ClassLoader {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault();

        // 获取监控目标基础信息
        CtClass ctClass = pool.get(TestD.class.getName());
        // 修改类名
        ctClass.replaceClassName("TestD","NewTestD");
        String className = ctClass.getName();
        // 获取方法
        CtMethod ctMethod = ctClass.getDeclaredMethod("strToInt");
        String methodName = ctMethod.getName();
        // 方法信息
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        // 方法内局部属性 参数信息
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        CtClass[] parameterTypes = ctMethod.getParameterTypes();

        boolean isStatic = (methodInfo.getAccessFlags() & AccessFlag.STATIC) != 0; // 是否为静态方法
        int parameterSize = isStatic ? attr.tableLength() : attr.tableLength() - 1;
        List<String> parameterNameList = new ArrayList<String>(parameterSize); // 参数名称
        List<String> parameterTypeList = new ArrayList<String>(parameterSize); // 参数类型
        StringBuilder parameters = new StringBuilder();

        for (int i = 0; i < parameterSize; i++) {
            parameterNameList.add(attr.variableName(i + (isStatic ? 0 : 1))); // 去除静态方法第一个参数 第一个参数为this
            parameterTypeList.add(parameterTypes[i].getName());
            if (i + 1 == parameterSize) {
                parameters.append("$").append(i + 1);
            }else{
                parameters.append("$").append(i + 1).append(",");
            }
        }

        //获取返回值类型 类型名称
        CtClass returnType = ctMethod.getReturnType();
        String returnTypeName = returnType.getName();

        // 获取方法ID
        int methodId = Monitor.generateMethodId(className,methodName,parameterNameList,parameterTypeList,returnTypeName);

        // 打桩 为目标方法添加增加字节
        ctMethod.addLocalVariable("startNanos",CtClass.longType);
        ctMethod.addLocalVariable("parameterValues",pool.get(Object[].class.getName()));
        // 添加计时相关代码
        // 方法前加强
        ctMethod.insertBefore("{ startNanos = System.nanoTime(); parameterValues = new Object[]{" + parameters.toString() + "}; }");
        // 方法后加强
        ctMethod.insertAfter("{ org.example.learn.javassist.monitor.Monitor.point(" + methodId + ", startNanos, parameterValues, $_); }",false);
        // 为方法添加TryCatch 异常捕获代码
        ctMethod.addCatch("{ org.example.learn.javassist.monitor.Monitor.point(" + methodId + ", $e); throw $e; }",ClassPool.getDefault().get("java.lang.Exception"));

        // 输出增加后的类内容
        ctClass.writeFile();

        byte[] bytes = ctClass.toBytecode();
        Class<?> classNew = new GenerateClassMethod().defineClass("org.example.learn.javassist.monitor.TestD",bytes,0,bytes.length);

        Method method = classNew.getMethod("strToInt",String.class,String.class);
        // 正常参数
        Object val1 = method.invoke(classNew.newInstance(),"10","12");
        System.out.println(val1);
        // 错误参数
        Object val2 = method.invoke(classNew.newInstance(),"zhang","1");
        System.out.println(val2);
    }
}
